package intopo.game.impl.givetake;

import java.io.PrintStream;
import org.w3c.dom.Element;

import util.xml.*;

import intopo.game.Game;

/**
 * This class represents a Give-Take game. This game is played by two
 * players who take turns sharing the benifits of a durable and indivisible resource.  The player that has the resource can give it or do nothing.  The player without the resource can take it or do nothing.  The
 * parameters of this game are the costs associated to taking the resource and
 * the bonus for giving the resource.  The value of the resource is constant and set to 1. 
 * 
 * <p> The game lasts a certain number of iterations.  This depends on the particular implemention.  In this package, there are two implementations.  One simulates a given number of iterations.
 * The other assumes the game has an infinite number of iterations.  In this case we take into account the average time a player keeps the resource before giving and the average time a players stays without the resource before taking.
 *
 * <p>Class {@code GiveTakeStrategy} is an abstraction of a strategy to play
 * this game and provides the basic methods to simulate a Give-Take game.
 *
 * <h3> Configuration File </h3>
 *
 * <p> The text file
 * 
 * @author <a href="mailto:plsm@det.ua.pt">Pedro Lopes da Silva Mariano</a>
 * @version 1.0
 * @see <a href="http://labmag.di.fc.ul.pt/~plsm/research/givetake/">Give-Take applet</a>
 * @see GiveTakeStrategy
 * @see GiveTakeHistory
 */
public class GiveTakeGame
	implements Game<GiveTakeStrategy>
{
	static String CONSTRUCTOR_ANA_TAG = "GiveTakeGame";
	/**
	 * Amount wield by the player that plays the give action.
	 */
	protected final float giveBonus;
	/**
	 * Amount payed by the player that plays the take action.
	 */
	protected final float takePerformerCost;
	/**
	 * Amount payed by the player that is subject of the take action.
	 */
	protected final float takeSubjectCost;

	/**
	 * Construct a {@code GiveTakeGame} with the given game parameters.

	 * @param giveBonus the bonus obtained by playing the give action
	 * @param takePerformerCost the cost of performing the take action
	 * @param takeSubjectCost the cost payed by the subject of the take action
	 */
	public GiveTakeGame (float giveBonus,
								float takePerformerCost,
								float takeSubjectCost)
	{
		assert 0 <= giveBonus && giveBonus < 1;
		assert takePerformerCost >= 0;
		assert takeSubjectCost >= 0;

		this.giveBonus = giveBonus;
		this.takePerformerCost = takePerformerCost;
		this.takeSubjectCost = takeSubjectCost;
	}

	/**
	 * Construct a {@code GiveTakeGame} object whose fields are taken from
	 * the specified XML element. This element must have the following
	 * attributes:

	 * <li>{@code giveBonus} a float number</li>

	 * <li>{@code takePerformerCost} a float number</li>

	 * <li>{@code takeSubjectCost} a float number</li>

	 * <p>The constructor doest not check if there any extra tags or attributes
	 * in {@code element} element.

	 * @param element the XML element where the required attributes are
	 * searched.

	 * @throws XMLParseException if the XML element does not have the
	 * required attributes or an error occured while parsing a required
	 * attribute value.
	 */
	public GiveTakeGame (Element element)
		throws XMLParseException
	{
		this
			(AttributeParser.getDouble ().parse (element, "giveBonus").floatValue (),
			 AttributeParser.getDouble ().parse (element, "takePerformerCost").floatValue (),
			 AttributeParser.getDouble ().parse (element, "takeSubjectCost").floatValue ());
	}
	/**
	 *
	 * @param scanner
	 */
	public GiveTakeGame (java.util.Scanner scanner)
	{
		this (scanner.nextFloat (), scanner.nextFloat (), scanner.nextFloat ());
	}
	/**
	 * The number of players in the Give-Take game is 2.
	 * @return the value {@code 2}.
	 */
	final public int numberPlayers ()
	{
		return 2;
	}

	/**
	 * Get the cost payed by the performer of the given action.
	 */
	final public float getActionCostForPerformer (GiveTakeAction action)
	{
		switch (action) {
		case NONEG:
			return 0;
		case GIVE:
			return -giveBonus;
		case NONET:
			return 0;
		case TAKE:
			return takePerformerCost;
		}
		throw new AssertionError ("Invalid GiveTakeAction parameter");
	}

	/**
	 * Get the cost payed by the subject of the given action.
	 */
	final public float getActionCostForSubject (GiveTakeAction action)
	{
		switch (action) {
		case NONEG:
			return 0;
		case GIVE:
			return 0;
		case NONET:
			return 0;
		case TAKE:
			return takeSubjectCost;
		}
		throw new AssertionError ("Invalid GiveTakeAction parameter");
	}

	/**
	 * Get the resource value.
	 */
	final public float getResourceValue ()
	{
		return 1;
	}


	public void play (Strategies<GiveTakeStrategy> players)
	{
//		if (players.size () != payoff.length)
//			throw new IllegalArgumentException ("The number of elements in the vectors players and payoff should be equal to 2");
//		if (players.size () != 2 && payoff.length != 2)
//			throw new IllegalArgumentException ("The number of elements in the vectors players and payoff is not 2");
//		if (payoff.length != 2)
//			throw new IllegalArgumentException ("The number of elements in the vector payoff is not 2");
//		if (players.size () != 2)
//			throw new IllegalArgumentException ("The number of elements in the vector players is not 2");

		int tr1, tnr1;
		int tr2, tnr2;
		float f1, f2;
		int diff1g2t, diff1t2g;
		GiveTakeStrategy p1, p2;
		
		p1 = players.getStrategy (0);
		p2 = players.getStrategy (1);
		diff1g2t = p1.averageGive () - p2.averageTake ();
		diff1t2g = p1.averageTake () - p2.averageGive ();
		
		if (diff1g2t < 0) {
			tr1 = p1.averageGive ();
			f1 = giveBonus;
			f2 = 0;
		}
		else if (diff1g2t == 0) {
			tr1 = p1.averageGive ();
			f1 = giveBonus - takeSubjectCost;
			f2 = -takePerformerCost;
		}
		else {
			tr1 = p2.averageTake ();
			f1 = -takeSubjectCost;
			f2 = -takePerformerCost;
		}
		if (diff1t2g < 0) {
			tnr1 = p1.averageTake ();
			f1 += -takePerformerCost;
			f2 += -takeSubjectCost;
		}
		else if (diff1t2g == 0) {
			tnr1 = p1.averageTake ();
			f1 += -takePerformerCost;
			f2 += giveBonus - takeSubjectCost;
		}
		else {
			tnr1 = p2.averageGive ();
			f2 += giveBonus;
		}
		tr2 = tnr1;
		tnr2 = tr1;
		players.setPayoff (0, (tr1 + f1 + 1) / (tr1 + tnr1 + 2));
		players.setPayoff (1, (tr2 + f2 + 1) / (tr2 + tnr2 + 2));
	}

	final public float lowestPayoff ()
	{
		return (1 - takePerformerCost - takeSubjectCost) / 2;
	}

	final public float highestPayoff ()
	{
		return 1;
	}

	final public float paretoPayoff ()
	{
		return (1 + giveBonus) / 2;
	}
	@Override
	public void write (PrintStream ps)
	{
		ps.print (CONSTRUCTOR_ANA_TAG);
		ps.print (' ');
		ps.print (this.giveBonus);
		ps.print (' ');
		ps.print (this.takePerformerCost);
		ps.print (' ');
		ps.print (this.takeSubjectCost);
	}

	public void debug ()
	{
		System.out.println ("GiveTakeGame");
		System.out.println ("bg = " + giveBonus);
		System.out.println ("cpt = " + takePerformerCost);
		System.out.println ("cst = " + takeSubjectCost);
		System.out.println ("Lowest payoff  = " + lowestPayoff ());
		System.out.println ("Highest payoff = " + highestPayoff ());
		System.out.println ("Pareto payoff = " + paretoPayoff ());
	}

	public String toString ()
	{
		String result;
		result = "bg = " + giveBonus;
		result += "\tcpt = " + takePerformerCost;
		result += "\tcst = " + takeSubjectCost;
		result += "\tLowest payoff  = " + lowestPayoff ();
		result += "\tHighest payoff = " + highestPayoff ();
		result += "\tPareto payoff = " + paretoPayoff ();
		return result;
	}
}
